<?php

/**
 * Erra_System_XmlHttpRequest - Main application router
 *
 * @package
 * @author Alexis Eric
 * @copyright Copyright (c) 2012 - Erra Prod
 * @version $1.0$
 * @access public
 */
class Erra_System_XmlHttpRequest {

    public static function handle($args) {
        // We first use language parameters if exists
        $args = trim($args, '/');
        $args_array = explode('/', $args);
        if (in_array($args_array[0], array_keys(Erra_Core_Locale::getAvailableLangs()))) {
            DEFINE('LANG', $args_array[0]);
            array_shift($args_array);
            if (count($args_array))
                $args = implode('/', $args_array);
            else
                $args = '';
        } else {
            DEFINE('LANG', Erra_Core_Locale::getDefaultLang()); // if no language is defined, english is the default
        }

        $realUrl = $args;
        $args = explode('/', $args);
        Erra_Core_Locale::set(LANG);
        $xmlResponse = new Erra_Core_XMLResponse();
        $ok = false;
        if (config_get('debug.log_messages')) {
            FB::log('[Erra_System_XmlHttpRequest] - Request transmitted by $_POST : ' . $realUrl);
        }

        // We handle $_POST data only if there are enough parameters
        // Correct format is : ActionFile/actionAction
        if (is_array($args) && count($args) == 2 && is_string($args[0]) && is_string($args[1])) {
            $actionCallback = $args[0] . '/' . $args[1];
            $actionClass = $args[0];
            $actionFunction = $args[1];
            $callback = array($actionClass, $actionFunction);
            // Get our field__data from $_POST
            $postArray = Erra_System_FormRenderer::getFieldPostData();
            $_POST = stripslashes_recursive($_POST);

            // Possible callbacks for list objects
            $listCallbacks = array('addInstance', 'deleteInstance', 'searchModule', 'editListInPlace', 'refreshList', 'changePosition');
            if (is_callable($callback) && (in_array($actionFunction, $listCallbacks) || get_parent_class($actionClass) == 'Erra_System_CallbackAction')) {
                $ajax_args = array_slice($args, 2);
                $objectId = $postArray['object_id'];
                $objectClass = $postArray['object_class'];
                $html_attributes = json_decode($postArray['html_attributes']);
                $description = $postArray['description'];
                $help = $postArray['help'];
                $buildArgs = !empty($postArray['build_args']) ? Erra_Serialization_SerializerJson::unserializeRaw(html_entity_decode($postArray['build_args'])) : null;
                $renderEngine = !empty($postArray['render_engine']) ? $postArray['render_engine'] : null;
                $returnCode = call_user_func_array(array($actionClass, $actionFunction), array());
                // Set search criteria in session if they exists
                if (isset($_POST[$objectId])) {
                    foreach ($_POST[$objectId] as $key => $value) {
                        if (strstr($key, 'search__')) {
                            $realFieldKey = str_replace('search__', '', $key);
                            $_SESSION['search__'][$objectId][$realFieldKey] = $value;
                        }
                    }
                }
                // We rebuild the field now the function is executed
                $field = Erra_System_Factory::getFieldList($objectClass, null, $buildArgs);
                $field->setRenderEngine($renderEngine);
                $field->addAttributes($html_attributes);
                $field->setDescription($description);
                $field->setHelp($help);
                $field->_postListInit();
                self::analyseResult($returnCode, $actionCallback, $field, $xmlResponse);
                // Here it's a callback for single object
            } else if (is_callable($callback) && isset($postArray['object_class']) && isset($postArray['object_id'])) {
                $ajax_args = array_slice($args, 2);
                $objectId = $postArray['object_id'];
                $objectClass = $postArray['object_class'];
                $description = $postArray['description'];
                $help = $postArray['help'];
                $html_attributes = json_decode($postArray['html_attributes']);
                $id = isset($postArray['id']) ? $postArray['id'] : -1;
                $buildArgs = !empty($postArray['build_args']) ? Erra_Serialization_SerializerJson::unserializeRaw(html_entity_decode($postArray['build_args'])) : null;
                $renderEngine = !empty($postArray['render_engine']) ? $postArray['render_engine'] : null;
                $field = null;
                if (class_exists($objectClass)) {
                    $field = new $objectClass($objectId, $buildArgs);
                    $field->setId($id);
                    $field->setObjectId($objectId);
                    $field->setName($objectId);
                    $field->addAttributes($html_attributes);
                    $field->setDescription($description);
                    $field->setHelp($help);
                }
                $field->setRenderEngine($renderEngine);
                if (isset($_POST[$objectId])) {
                    $field->setRawData($_POST[$objectId]);
                    $field->buildHtmlNames();
                } else {
                    $itemKey = $field->getObjectClass() . '.' . $field->getObjectId();
                    if (Erra_Persistance_Session::bKeyExists($itemKey)) {
                        $field->setRawData(Erra_Persistance_Session::getFieldData($itemKey));
                    }
                }

                // Call the action callback
                AdminAction::uploadImage($field);
                $returnCode = call_user_func_array(array($actionClass, $actionFunction), array($field));
                // Take appropriate action depending the result

                $field->postInit();
                self::analyseResult($returnCode, $actionCallback, $field, $xmlResponse);
            } else if (!is_callable($callback)) {
                // WARNING : THE AJAX CLASS CANT CALL APPROPRIATE CONTROLLER METHOD
                Erra_Core_Kernel::log(__('[Erra_System_XmlHttpRequest] - Can\'t call action method') . ' `' . $callback[0] . '->' . $callback[1] . '()`');
            }
        } else {
            // WARNING : THE AJAX CLASS CANT CALL APPROPRIATE CONTROLLER METHOD
            Erra_Core_Kernel::log(__('[Erra_System_XmlHttpRequest] - ' . __("Can't call action method based on following string") . ' : `' . $realUrl . '`, ' . __('because parameters are not well formatted (correct format is ActionClass/actionMethod);')));
        }
        header('Content-type: text/xml; charset=utf-8');
        if (!$ok) {
            $xmlResponse->status('1-0');
        }
        ob_clean();
        print trim($xmlResponse->asXML());
        exit;
    }

    /**
     * Erra_System_XmlHttpRequest::analyseResult() - Analyse result from action and choose the right action
     *
     * @param mixed $returnCode
     * @param mixed $actionCallback
     * @param mixed $field
     * @param mixed $xmlResponse
     * @return
     */
    public static function analyseResult($returnCode, $actionCallback, Erra_Fields_Base_Object &$field, &$xmlResponse) {
        // Reload the whole page
        if ($returnCode == 'RELOAD') {
            // Store message in session here
            if (strlen($field->getMessage())) {
                Erra_Persistance_Session::set('[@message@]' . $field->getObjectId(), $field->getMessage());
            }
            $xmlResponse->addReload();
            // Reload field in ajax
        } else if (strstr($returnCode, 'FORM_CLOSE_POPUP')) {
            $code = (strstr($returnCode, 'FORM_CLOSE_POPUP::')) ? str_replace('FORM_CLOSE_POPUP::', '', $returnCode) : '';
            $xmlResponse->addClosePopup($code);
            // Reload field in ajax
        } else if ($returnCode == 'FORM_REFRESH_FIELD') {
            $postArray = Erra_System_FormRenderer::getFieldPostData();
            $objectId = $postArray['object_id'];
            $attachedMethod = $actionCallback = $postArray['method'];
            $res = '<a name="' . $field->getObjectId() . '" id="' . $field->getObjectId() . '"></a>';
            if (strlen($field->getAttachedActionMethod())) {
                $actionCallback = $field->getAttachedActionMethod();
                if (strstr(LANG . '/', $attachedMethod)) {
                    $actionCallback = LANG . '/' . $actionCallback;
                }
            }
            $res .= $field->_form($actionCallback);
            $res .= self::generateTopScroll($field);
            $objectId = 'FIELD_' . $objectId;
            $xmlResponse->addReplace('#' . $objectId, $res);
            // Reload field list in ajax
        } else if (strstr($returnCode, 'FORM_REFRESH_FIELD_LIST')) {
            $returnString = (strstr($returnCode, ':') !== FALSE) ? explode(':', $returnCode) : $returnCode;
            $arglist = '';
            $msgList = '';
            $res = '';
            // Is there a message or not ?
            if (is_array($returnString) && count($returnString) >= 2) {
                $arglist = explode('=>', $returnString[0]);
                $msgList = explode('=>', $returnString[1]);
            } else if (is_string($returnString) && strstr($returnString, '=>')) {
                $arglist = explode('=>', $returnString);
            }
            if (is_array($msgList) && count($msgList) == 2) {
                $res = '<a name="' . $field->getObjectId() . '" id="' . $field->getObjectId() . '"></a>';
                $field->setMessage(Erra_Utils_Tools::message(trim($msgList[0]), trim($msgList[1])));
                $res .= self::generateTopScroll($field);
            }
            if (is_array($arglist) && count($arglist) == 2) {
                $actionCallback = (strlen($field->getAttachedActionMethod())) ? $field->getAttachedActionMethod() : trim($arglist[1]);
                if (strstr(LANG . '/', $arglist[1])) {
                    $actionCallback = LANG . '/' . $actionCallback;
                }
                $res .= $field->_form($actionCallback);
                $xmlResponse->addReplace('#FIELD_LIST_' . $field->getObjectClass(true), $res);
            }
            // Redirection (eg REDIRECT::user/profile/5)
        } else if (strstr($returnCode, 'REDIRECT::')) {
            $path = str_replace('REDIRECT::', '', $returnCode);
            $real_path = Erra_System_PageBuilder::makeLink($path);
            $real_path = (substr($real_path, - 1) != "/") ? $real_path . '/' : $real_path;
            $xmlResponse->addRedirection($real_path);
            // Internally used for edit in place
        } else if (strstr($returnCode, 'EDIT_IN_PLACE')) {
            $arglist = explode('=>', $returnCode);
            if (is_array($arglist) && count($arglist) == 2) {
                // Retreive SQL data for this field
                $field = Erra_Persistance_PersistanceUnit::getFieldFromSQL($field);
                $daoTarget = trim($arglist[1]);
                // FIXME :: getTableName should be hashed in a more unique way
                $res = '<div id="EDIT_IN_PLACE_' . $field->getObjectId() . '" ' . $field->getHtmlAttributes() . ' data-object="' . $field->getTableName() . '">' . Erra_System_FormRenderer::editField($field, trim($daoTarget), true) . '</div>';
                $xmlResponse->addOpenPopup($res);
            }
        }
    }

    public static function generateTopScroll($field) {
        // FIXME : add more entropy if two fields are on the same page ?
        $scrollId = $field->getObjectId();
        $res = '<script type="text/javascript">';
        $res .= 'var target_offset = $("#' . $scrollId . '").offset();';
        $res .= 'var target_top = target_offset.top;';
        $res .= '$("html, body").animate({scrollTop:target_top}, 500);';
        $res .= '</script>';
        return $res;
    }

}

?>